package com.hunttown.common.sequence;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;

public class MySQLSequence implements InitializingBean {
    private static final Log log = LogFactory.getLog(MySQLSequence.class);
    private String sequenceTableName;
    private String sequenceKey;
    private String sequenceValue;
    private int blockSize = 5;
    private long startValue = 0L;
    private String getSQL = null;
    private String newSQL = null;
    private String updateSQL = null;
    private Map<String, MySQLSequence.Step> stepMap = new HashMap();
    private DataSource dataSource;

    public MySQLSequence() {
    }

    private boolean getNextBlock(String sequenceName, MySQLSequence.Step step) {
        Long value = this.getPersistenceValue(sequenceName);
        if (value == null) {
            try {
                value = this.newPersistenceValue(sequenceName);
            } catch (Exception var5) {
                log.error("newPersistenceValue error!");
                value = this.getPersistenceValue(sequenceName);
            }
        }

        boolean b = this.saveValue(value.longValue(), sequenceName) == 1;
        if (b) {
            step.setCurrentValue(value.longValue());
            step.setEndValue(value.longValue() + (long)this.blockSize);
        }

        return b;
    }

    public synchronized long get(String sequenceName) {
        MySQLSequence.Step step = (MySQLSequence.Step)this.stepMap.get(sequenceName);
        if (step == null) {
            step = new MySQLSequence.Step(this.startValue, this.startValue + (long)this.blockSize);
            this.stepMap.put(sequenceName, step);
        } else if (step.currentValue < step.endValue) {
            return step.incrementAndGet();
        }

        for(int i = 0; i < this.blockSize; ++i) {
            if (this.getNextBlock(sequenceName, step)) {
                return step.incrementAndGet();
            }
        }

        throw new RuntimeException("No more value.");
    }

    private int saveValue(long value, String sequenceName) {
        Connection connection = null;
        PreparedStatement statement = null;

        int var6;
        try {
            connection = this.dataSource.getConnection();
            statement = connection.prepareStatement(this.updateSQL);
            statement.setLong(1, value + (long)this.blockSize);
            statement.setString(2, sequenceName);
            statement.setLong(3, value);
            var6 = statement.executeUpdate();
        } catch (Exception var18) {
            log.error("newPersistenceValue error!", var18);
            throw new RuntimeException("newPersistenceValue error!", var18);
        } finally {
            if (statement != null) {
                try {
                    statement.close();
                } catch (SQLException var17) {
                    log.error("close statement error!", var17);
                }
            }

            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException var16) {
                    log.error("close connection error!", var16);
                }
            }

        }

        return var6;
    }

    private Long getPersistenceValue(String sequenceName) {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            connection = this.dataSource.getConnection();
            statement = connection.prepareStatement(this.getSQL);
            statement.setString(1, sequenceName);
            resultSet = statement.executeQuery();
            if (resultSet.next()) {
                Long var5 = resultSet.getLong(this.sequenceValue);
                return var5;
            }
        } catch (Exception var23) {
            log.error("getPersistenceValue error!", var23);
            throw new RuntimeException("getPersistenceValue error!", var23);
        } finally {
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException var22) {
                    log.error("close resultset error!", var22);
                }
            }

            if (statement != null) {
                try {
                    statement.close();
                } catch (SQLException var21) {
                    log.error("close statement error!", var21);
                }
            }

            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException var20) {
                    log.error("close connection error!", var20);
                }
            }

        }

        return null;
    }

    private Long newPersistenceValue(String sequenceName) {
        Connection connection = null;
        PreparedStatement statement = null;

        try {
            connection = this.dataSource.getConnection();
            statement = connection.prepareStatement(this.newSQL);
            statement.setLong(1, this.startValue);
            statement.setString(2, sequenceName);
            statement.executeUpdate();
        } catch (Exception var15) {
            log.error("newPersistenceValue error!", var15);
            throw new RuntimeException("newPersistenceValue error!", var15);
        } finally {
            if (statement != null) {
                try {
                    statement.close();
                } catch (SQLException var14) {
                    log.error("close statement error!", var14);
                }
            }

            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException var13) {
                    log.error("close connection error!", var13);
                }
            }

        }

        return this.startValue;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void setBlockSize(int blockSize) {
        this.blockSize = blockSize;
    }

    public void setStartValue(long startValue) {
        this.startValue = startValue;
    }

    public void setSequenceTableName(String sequenceTableName) {
        this.sequenceTableName = sequenceTableName;
    }

    public void setSequenceKey(String sequenceKey) {
        this.sequenceKey = sequenceKey;
    }

    public void setSequenceValue(String sequenceValue) {
        this.sequenceValue = sequenceValue;
    }

    public void afterPropertiesSet() throws Exception {
        if (this.dataSource == null) {
            throw new IllegalArgumentException("dataSource cann't be null or empty in MySQLSequence!");
        } else if (this.sequenceTableName != null && !"".equals(this.sequenceTableName) && this.sequenceKey != null && !"".equals(this.sequenceKey) && this.sequenceValue != null && !"".equals(this.sequenceValue)) {
            this.getSQL = "select " + this.sequenceValue + " from " + this.sequenceTableName + " where " + this.sequenceKey + " = ?";
            this.newSQL = "insert into " + this.sequenceTableName + " (" + this.sequenceValue + "," + this.sequenceKey + ") values (?,?)";
            this.updateSQL = "update " + this.sequenceTableName + " set " + this.sequenceValue + " = ?  where " + this.sequenceKey + " = ? and " + this.sequenceValue + " = ?";
        } else {
            throw new IllegalArgumentException("this sequence table config can't be null or empty in MySQLSequence!");
        }
    }

    static class Step {
        private long currentValue;
        private long endValue;

        Step(long currentValue, long endValue) {
            this.currentValue = currentValue;
            this.endValue = endValue;
        }

        public void setCurrentValue(long currentValue) {
            this.currentValue = currentValue;
        }

        public void setEndValue(long endValue) {
            this.endValue = endValue;
        }

        public long incrementAndGet() {
            return ++this.currentValue;
        }
    }
}